using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Diagnostics;

namespace WindowsGame1
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        const float GRAVITY = -0.2f;
        const float JUMPVELOCITY = 4;
        const int MAZESIDELENGTH = 10;
        const int TELEPORTCOOLDOWN = 10;
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Model stoneWall;
        Model ground;
        Model radio;
        Monster monster;
        Model wand;
        //Texture3D monsterTexture;
        float rotation;
        float verticalRotation;
        float verticalVelocity;
        Vector3 cameraLocation;
        Vector3 wandLocation;
        float wandZOffset, wandXOffset, wandYOffset;
        float wandZRot, wandXRot, wandYRot;
        Song song;
        Maze maze;
        int groundScale;
        List<BoundingBox> wallsBoundingBoxes;
        Vector3 startingLocation;
        Vector3 endLocation;
        float rotateConst = 3 * MathHelper.ToRadians(1.0f);
        Stopwatch teleportCooldown;
        Stopwatch stopwatch;
        Stopwatch monsterWatch;
        private Texture2D backgroundTexture;
        SpriteBatch ForegroundBatch;
        SpriteFont CourierNew;
        Vector2 FontPos;
        //float FontRotation;

        float radius;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            rotation = 0;
            verticalRotation = 0;
            verticalVelocity = 0;
            wandZOffset = 20; // 10
            wandXOffset = 7; // 14
            wandYOffset = 5;
            wandXRot = 180;//150
            wandZRot = 0; //70
            wandYRot = 10;
            cameraLocation = new Vector3(100, 25, 100);
            wandLocation = new Vector3(cameraLocation.X + wandXOffset, cameraLocation.Y + wandYOffset, cameraLocation.Z + wandZOffset);
            
            song = Content.Load<Song>("tumblr_lb172mjmmR1qb41fxo1");
            MediaPlayer.IsRepeating = true;
            MediaPlayer.Play(song);
            // Scaled by 1, the length of the walls is 200
            maze = new Maze(MAZESIDELENGTH, MAZESIDELENGTH);
            groundScale = MAZESIDELENGTH * 3;
            startingLocation = cameraLocation;
            Random r = new Random();
            endLocation = new Vector3(r.Next(3, MAZESIDELENGTH)  * 200 - 100, 0, r.Next(2, MAZESIDELENGTH) * 200 - 100);
            MediaPlayer.Volume = GetMediaPlayerVolume();
            wallsBoundingBoxes = new List<BoundingBox>();
            teleportCooldown = new Stopwatch();
            teleportCooldown.Start();
            stopwatch = new Stopwatch();
            stopwatch.Start();
            monsterWatch = new Stopwatch();
            monsterWatch.Start();
            monster = new Monster(new Vector3(MAZESIDELENGTH * 200 - 100, 0, MAZESIDELENGTH * 200 - 100));
            radius = 21f;


            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            stoneWall = Content.Load<Model>("wallstone");
            ground = Content.Load<Model>("terrain");
            radio = Content.Load<Model>("Radio_Tivoli");
            wand = Content.Load<Model>("hikingstick3");
            this.backgroundTexture = Content.Load<Texture2D>("hd-starry-sky");
            monster.Load(Content);
            foreach (Cell c in maze.GetCells())
            {
                foreach (Wall w in c.GetAllWalls())
                {
                    w.Load(Content);
                }
            }

            CourierNew = Content.Load<SpriteFont>("SpriteFont1");
            ForegroundBatch = new SpriteBatch(graphics.GraphicsDevice);
            FontPos = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2,
                graphics.GraphicsDevice.Viewport.Height / 2);
            //FontRotation = 0;
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {

            if (maze.sameCell(cameraLocation, endLocation))
            {
                //DisplayEndScreen(gameTime, "You win!");
                Initialize();
                return;
            }
            if (maze.sameCell(cameraLocation, monster.GetLocation()))
            {
                //DisplayEndScreen(gameTime, "You lose...");
                Initialize();
                return;
            }

            Vector3 wandBBOffset = new Vector3(25, 0, -25);

            TimeSpan ts = stopwatch.Elapsed;
            if (ts.Seconds >= 12)
            {
                stopwatch.Stop();
                maze.Update(cameraLocation);
                UpdateBoundingBoxesList(); // in case a wall has moved
                stopwatch.Restart();
            }
            GamePadState state = GamePad.GetState(PlayerIndex.One);
            float x = state.ThumbSticks.Left.X * (float)Math.Cos(rotation)
                - state.ThumbSticks.Left.Y * (float)Math.Sin(rotation);
            float z = -state.ThumbSticks.Left.Y * (float)Math.Cos(rotation)
                - state.ThumbSticks.Left.X * (float)Math.Sin(rotation);

            KeyboardState kState = Keyboard.GetState(PlayerIndex.One);
            Keys[] keys = kState.GetPressedKeys();

            if (keys.Contains<Keys>(Keys.E))
            {
               
                cameraLocation.X += 5 * (float)(Math.Cos(rotation));
                cameraLocation.Z -= 5 * (float)(Math.Sin(rotation));

                if(CheckCollision(new BoundingSphere(cameraLocation, radius)))
                {
                    cameraLocation.X -= 5 * (float)(Math.Cos(rotation));
                    cameraLocation.Z += 5 * (float)(Math.Sin(rotation));
                }
                wandLocation.X = cameraLocation.X + wandXOffset;
                wandLocation.Y = cameraLocation.Y + wandYOffset;
                wandLocation.Z = cameraLocation.Z + wandZOffset;
            }

            if (keys.Contains<Keys>(Keys.Q))
            {
                cameraLocation.X -= 5 * (float)(Math.Cos(rotation));
                cameraLocation.Z += 5 * (float)(Math.Sin(rotation));

                if(CheckCollision(new BoundingSphere(cameraLocation, radius)))
                {
                    cameraLocation.X += 5 * (float)(Math.Cos(rotation));
                    cameraLocation.Z -= 5 * (float)(Math.Sin(rotation));
                }
                wandLocation.X = cameraLocation.X + wandXOffset;
                wandLocation.Y = cameraLocation.Y + wandYOffset;
                wandLocation.Z = cameraLocation.Z + wandZOffset;
            }

            if (keys.Contains<Keys>(Keys.W))
            {
                
                cameraLocation.X -= 5 * (float)(Math.Sin(rotation));
                cameraLocation.Z -= 5 * (float)(Math.Cos(rotation));
                
                if(CheckCollision(new BoundingSphere(cameraLocation, radius)))
                {
                    cameraLocation.X += 5 * (float)(Math.Sin(rotation));
                    cameraLocation.Z += 5 * (float)(Math.Cos(rotation));
                }
                wandLocation.X = cameraLocation.X + wandXOffset;
                wandLocation.Y = cameraLocation.Y + wandYOffset;
                wandLocation.Z = cameraLocation.Z + wandZOffset;
                
            }

            if (keys.Contains<Keys>(Keys.S))
            {
                
                cameraLocation.X += 5 * (float)(Math.Sin(rotation));
                cameraLocation.Z += 5 * (float)(Math.Cos(rotation));
                
                if(CheckCollision(new BoundingSphere(cameraLocation, radius)))
                {
                    cameraLocation.X -= 5 * (float)(Math.Sin(rotation));
                    cameraLocation.Z -= 5 * (float)(Math.Cos(rotation));
                }

                wandLocation.X = cameraLocation.X + wandXOffset;
                wandLocation.Y = cameraLocation.Y + wandYOffset;
                wandLocation.Z = cameraLocation.Z + wandZOffset;
               
            }

            if (keys.Contains<Keys>(Keys.Left) || keys.Contains<Keys>(Keys.A))
            {
                rotation += rotateConst;
            }

            if (keys.Contains<Keys>(Keys.Right) || keys.Contains<Keys>(Keys.D))
            {
                rotation -= rotateConst;
            }

            if (keys.Contains<Keys>(Keys.Up))
            {
                if (verticalRotation - rotateConst > -Math.PI / 2)
                {
                    verticalRotation -= rotateConst;
                }
            }

            if (keys.Contains<Keys>(Keys.Down))
            {
                if (verticalRotation + rotateConst < Math.PI / 2)
                {
                    verticalRotation += rotateConst;
                }
            }
            
            if (keys.Contains<Keys>(Keys.Space) || state.Buttons.A == ButtonState.Pressed)
            {
                if (cameraLocation.Y == 25)
                {
                    verticalVelocity = JUMPVELOCITY;
                }

            }

            if (keys.Contains<Keys>(Keys.T) || state.Buttons.B == ButtonState.Pressed)
            {
                TimeSpan span = teleportCooldown.Elapsed;
                if (span.Seconds >= TELEPORTCOOLDOWN)
                {
                    Teleport();
                    teleportCooldown.Restart();
                }
                else
                {
                    Console.WriteLine(span.Seconds);
                }
            }

            cameraLocation.Y += verticalVelocity;

            if (cameraLocation.Y < 25)
            {
                cameraLocation.Y = 25;
            }

            if (cameraLocation.Y != 25)
            {
                verticalVelocity += GRAVITY;
            }

            //if (keys.Contains<Keys>(Keys.LeftShift))
            //{
            //    cameraLocation.Y -= 5;
            //}

            rotation += 0.05f * (-state.ThumbSticks.Right.X);

            float newVRotation = verticalRotation + 0.05f * (state.ThumbSticks.Right.Y);

            if (Math.Abs(newVRotation) < Math.PI / 2)
            {
                verticalRotation = newVRotation;
            }


            cameraLocation.X += x * 2; // TODO I added these 2's so controller movement goes faster
            cameraLocation.Z += z * 2;

            if (CheckCollision(new BoundingSphere(cameraLocation, radius)))
            {
                cameraLocation.X -= x * 2;
                cameraLocation.Z -= z * 2;
            }
            wandLocation.X = cameraLocation.X + wandXOffset;
            wandLocation.Y = cameraLocation.Y + wandYOffset;
            wandLocation.Z = cameraLocation.Z + wandZOffset;

            MediaPlayer.Volume = GetMediaPlayerVolume();

            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            TimeSpan tsMonster = monsterWatch.Elapsed;
            if (tsMonster.Seconds >= 4)
            {
                monsterWatch.Stop();
                Vector3 changeInLocation = maze.FindNextCellToProceedTo(monster.GetLocation(), cameraLocation);
                //Console.Out.WriteLine("Changing monster's position,with a change of " + changeInLocation);
                monster.MoveTowardsPlayer(changeInLocation);
                //    monster.SwingThighs();
                monsterWatch.Restart();
            }
            //float time = (float)gameTime.TotalGameTime.TotalSeconds; 
            //monster.WheelRotation = time * 5;
            //monster.SteerRotation = (float)Math.Sin(time * 0.75f) * 0.5f;
            //monster.TurretRotation = (float)Math.Sin(time * 0.333f) * 1.25f;
            //monster.CannonRotation = (float)Math.Sin(time * 0.25f) * 0.333f - 0.333f;
            //monster.HatchRotation = MathHelper.Clamp((float)Math.Sin(time * 2) * 2, -1, 0);
            //Console.WriteLine("I'm at " + cameraLocation.ToString() + ", Wand is at " + wandLocation);
            base.Update(gameTime);
        }

        //private void DisplayEndScreen(GameTime gameTime, string p)
        //{
        //    ForegroundBatch.Begin();
        //    while (true)
        //    {
        //        // Find the center of the string
        //        Vector2 FontOrigin = CourierNew.MeasureString(p) / 2;
        //        // Draw the string
        //        ForegroundBatch.DrawString(CourierNew, p, FontPos, Color.LightGreen,
        //            FontRotation, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
        //        //Note: Change "CourierNew" to "current" above to enable switching

        //        // Draw instructions
        //        Vector2 guidepos = new Vector2(50, FontPos.Y + 100);
        //        string guide = "Right Thumbstick: Rotate\nA, B, X Buttons: Change Font";
        //        ForegroundBatch.DrawString(CourierNew, guide, guidepos, Color.Black);

        //        ForegroundBatch.End();
        //        base.Draw(gameTime);
        //        // Display message p
        //        // Wait for them to press X
        //        break;
        //    }
        //    Initialize(); // start a new maze
        //}

        void Teleport()
        {
            while (rotation > 2 * Math.PI)
            {
                rotation -= 2 * (float) Math.PI;
            }

            while (rotation <= 0)
            {
                rotation += 2 * (float) Math.PI;
            }

            if (rotation <= Math.PI / 4 || rotation > 7 * Math.PI / 4)
            {
                // Move up one square
                if ((int) (cameraLocation.Z / 200) - 1 > 0)
                {
                    cameraLocation.Z = 200 * (int)(cameraLocation.Z / 200 - 1) + 100;
                    cameraLocation.X = 200 * (int)(cameraLocation.X / 200) + 100;
                    //cameraLocation.Z -= 200;
                }
            }

            if (rotation <= 3 * Math.PI / 4 && rotation > Math.PI / 4)
            {
                // Move left one square
                if ((int)(cameraLocation.X / 200) - 1 > 0)
                {
                    cameraLocation.X = 200 * (int)(cameraLocation.X / 200 - 1) + 100;
                    cameraLocation.Z = 200 * (int)(cameraLocation.Z / 200) + 100;
                    //cameraLocation.X -= 200;
                }
            }

            if (rotation <= 5 * Math.PI / 4 && rotation > 3 * Math.PI / 4)
            {
                // Move down one square
                if ((int)(cameraLocation.Z / 200) + 1 < maze.CountRows())
                {
                    cameraLocation.Z = 200 * (int)(cameraLocation.Z / 200 + 1) + 100;
                    cameraLocation.X = 200 * (int)(cameraLocation.X / 200) + 100;
                    //cameraLocation.Z += 200;
                }
            }

            if (rotation <= 7 * Math.PI / 4 && rotation > 5 * Math.PI / 4)
            {
                // Move right one square
                if ((int)(cameraLocation.X / 200) + 1 < maze.CountCols())
                {
                    cameraLocation.X = 200 * (int)(cameraLocation.X / 200 + 1) + 100;
                    cameraLocation.Z = 200 * (int)(cameraLocation.Z / 200) + 100;
                    //cameraLocation.X += 200;
                }
            }

        }

        float GetMediaPlayerVolume()
        {
            return (float) Math.Pow(1 - (endLocation - cameraLocation).Length() / (endLocation - startingLocation).Length(), 2);
        }
        
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(new Color(8, 8, 72, 225));

            //this.spriteBatch.Begin();
            //int screenWidth = graphics.GraphicsDevice.PresentationParameters.BackBufferWidth;
            //int screenHeight = graphics.GraphicsDevice.PresentationParameters.BackBufferHeight;
            //Rectangle screenRectangle = new Rectangle(0, 0, screenWidth, screenHeight);
            //Vector2 pos = new Vector2(0, 0);
            //this.spriteBatch.Draw(this.backgroundTexture, screenRectangle, Color.White);
            //this.spriteBatch.End();

            // DRAW THE WALLS
            Matrix[] transforms;
            Model wall;
            Vector3 gazeDirection = - (new Vector3((float)Math.Sin(rotation), (float) Math.Sin(verticalRotation), (float)Math.Cos(rotation)));
            wall = stoneWall;
            transforms = new Matrix[wall.Bones.Count];
            wall.CopyAbsoluteBoneTransformsTo(transforms); // Copy any parent transforms.

            foreach (Cell c in maze.GetCells())
            {
                foreach (Wall w in c.GetUpWalls())
                {
                    // TODO This fixes the problem if a wall tries to come up under us..., take out if more problems arise because of this
                    if (CheckCollision(w.GetBoundingBox(), new BoundingSphere(cameraLocation, radius)))
                    {
                        w.GetCell().Set(w.GetEdge(), false);
                        Cell adjCell = maze.GetAdjacentCell(w.GetCell(), w.GetEdge());
                        adjCell.Set(maze.GetOppositeEdge(w.GetEdge()), false);
                        adjCell.UpdateUpWalls();
                        continue;
                    }
                    //DOESN'T WORK, DON'T DO (WISH IT DID, BUT I WASN'T SMART ABOUT HOW I DETERMINED WHERE TO PLACE THE BLOCKS:
                    //w.Draw(Matrix.CreateRotationY(MathHelper.ToRadians(w.getRotation()))
                    //            * Matrix.CreateScale(1)
                    //            * Matrix.CreateTranslation(w.getPosition()),
                    //
                    //            Matrix.CreateLookAt(cameraLocation, cameraLocation -
                    //            new Vector3((float)Math.Sin(rotation), 0, (float)Math.Cos(rotation)), Vector3.Up),
                    //
                    //            Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
                    //            graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f));

                    if (true)
                    {
                        Vector3 modelPosition = w.getPosition();
                        float wallRotation = w.getRotation();
                        foreach (ModelMesh mesh in wall.Meshes)
                        {
                            // This is where the mesh orientation is set, as well as our camera and projection.
                            foreach (BasicEffect effect in mesh.Effects)
                            {
                                effect.EnableDefaultLighting();

                                
                                //effect.FogEnabled = true;
                                //effect.FogColor = new Vector3(0.0f, 0.0f, 0.0f);
                                //effect.FogStart = 0.1f;


                                effect.World = transforms[mesh.ParentBone.Index]
                                    * Matrix.CreateRotationY(MathHelper.ToRadians(wallRotation))
                                    * Matrix.CreateScale(1)
                                    * Matrix.CreateTranslation(modelPosition);

                                effect.View = Matrix.CreateLookAt(cameraLocation, cameraLocation +
                                    gazeDirection, Vector3.Up);
                                effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
                                    graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f);
                            }
                            mesh.Draw();
                        }
                    }

                    //// Following two lines for debugging for drawing just one wall (comment out upper two foreach's and updateBoundingBoxes in update() as well)
                    ////Wall w = new Wall(new Cell(0, 0), CellSide.LEFT);
                    ////wallsBoundingBoxes.Add(w.GetBoundingBox());
                    //if (false) // draw bounding box...for debugging purposes
                    //{
                    //    BasicEffect boxEffect = new BasicEffect(GraphicsDevice);
                    //    {
                    //        foreach (BoundingBox box in wallsBoundingBoxes)
                    //        {
                    //            Vector3[] corners = box.GetCorners();
                    //            VertexPositionColor[] primitiveList = new VertexPositionColor[corners.Length];
                    //
                    //            // Assign the 8 box vertices
                    //            for (int i = 0; i < corners.Length; i++)
                    //            {
                    //                primitiveList[i] = new VertexPositionColor(corners[i], Color.White);
                    //            }
                    //            // Initialize an array of indices for the box. 12 lines require 24 indices
                    //            short[] bBoxIndices = {
                    //            0, 1, 1, 2, 2, 3, 3, 0, // Front edges
                    //            4, 5, 5, 6, 6, 7, 7, 4, // Back edges
                    //            0, 4, 1, 5, 2, 6, 3, 7 // Side edges connecting front and back
                    //        };
                    //
                    //            /* Set your own effect parameters here */
                    //            boxEffect.LightingEnabled = false;
                    //            boxEffect.World = Matrix.Identity;
                    //            boxEffect.View = Matrix.CreateLookAt(cameraLocation, cameraLocation -
                    //                new Vector3((float)Math.Sin(rotation), 0, (float)Math.Cos(rotation)), Vector3.Up);
                    //            boxEffect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
                    //                graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f);
                    //            boxEffect.TextureEnabled = false;
                    //
                    //            // Draw the box with a LineList
                    //            foreach (EffectPass pass in boxEffect.CurrentTechnique.Passes)
                    //            {
                    //                pass.Apply();
                    //                GraphicsDevice.DrawUserIndexedPrimitives(
                    //                    PrimitiveType.LineList, primitiveList, 0, 8,
                    //                    bBoxIndices, 0, 12);
                    //            }
                    //        }
                    //    }
                    //}
                }
                // TODO This fixes the problem if a wall tries to come up under us..., take out if more problems arise because of this
                c.UpdateUpWalls();
                UpdateBoundingBoxesList();
            }

            // DRAW THE MONSTER
            monster.Draw(Matrix.CreateRotationY(MathHelper.ToRadians(monster.getBodyRotation()))
                        * Matrix.CreateScale(0.5f)
                        * Matrix.CreateTranslation(monster.GetLocation()),

                        Matrix.CreateLookAt(cameraLocation, cameraLocation +
                        gazeDirection, Vector3.Up),

                        Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
                        graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f));

            transforms = new Matrix[radio.Bones.Count];
            radio.CopyAbsoluteBoneTransformsTo(transforms);

            // DRAW THE RADIO
            foreach (ModelMesh mesh in radio.Meshes)
            {
                // This is where the mesh orientation is set, as well as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index]
                        * Matrix.CreateRotationY(MathHelper.ToRadians(225))
                        * Matrix.CreateScale(.5f)
                        * Matrix.CreateTranslation(endLocation);

                    effect.View = Matrix.CreateLookAt(cameraLocation, cameraLocation +
                        gazeDirection, Vector3.Up);
                    effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
                        graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f);
                }

                // Draw the mesh, using the effects set above.
                mesh.Draw();
            }

            // DRAW THE GROUND
            transforms = new Matrix[ground.Bones.Count];
            ground.CopyAbsoluteBoneTransformsTo(transforms);

            foreach (ModelMesh mesh in ground.Meshes)
            {
                // This is where the mesh orientation is set, as well as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();


                    //effect.FogEnabled = true;
                    //effect.FogStart = 0.0f;

                    effect.World = transforms[mesh.ParentBone.Index]
                        * Matrix.CreateScale(groundScale)
                        * Matrix.CreateTranslation(Vector3.Zero);

                    effect.View = Matrix.CreateLookAt(cameraLocation, cameraLocation +
                        gazeDirection, Vector3.Up);
                    effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
                        graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f);
                }

                // Draw the mesh, using the effects set above.
                mesh.Draw();
            }

            // DRAW THE WAND
            transforms = new Matrix[wand.Bones.Count];
            wand.CopyAbsoluteBoneTransformsTo(transforms);

            foreach (ModelMesh mesh in wand.Meshes)
            {
                // This is where the mesh orientation is set, as well as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index]
                        * Matrix.CreateRotationZ(MathHelper.ToRadians(wandZRot))
                        * Matrix.CreateRotationX(MathHelper.ToRadians(wandXRot))
                        * Matrix.CreateRotationY(MathHelper.ToRadians(wandYRot))
                        * Matrix.CreateScale(5)
                        * Matrix.CreateTranslation(wandLocation);
                    //new Vector3(cameraLocation.X + wandXOffset, 
                    //cameraLocation.Y + wandYOffset, 
                    //cameraLocation.Z + wandZOffset));

                    effect.View = Matrix.CreateLookAt(cameraLocation,
                        new Vector3(cameraLocation.X + wandXOffset,
                                    cameraLocation.Y,// + wandYOffset, 
                                    cameraLocation.Z + 10// + wandZOffset to point it straight to center of screen
                                                      ),
                        Vector3.Up);
                    effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
                        graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f);
                }

                // Draw the mesh, using the effects set above.
                mesh.Draw();
            }

            base.Draw(gameTime);
        }

        private bool CheckCollision(BoundingBox boundingBox, BoundingSphere boundingSphere)
        {
            if (boundingBox.Contains(boundingSphere) != ContainmentType.Disjoint)
            {
                //Console.Out.WriteLine("A wall tried to go up where I'm standing!");
                return true;
            }
            return false;
        }

        private void UpdateBoundingBoxesList()
        {
            wallsBoundingBoxes.Clear();
            foreach (Cell c in maze.GetCells())
            {
                foreach (Wall w in c.GetUpWalls())
                {
                    wallsBoundingBoxes.Add(w.GetBoundingBox());
                }
            }
        }

        private bool CheckCollision(BoundingSphere sphere)
        {
            if (sphere.Contains(monster.GetBoundingSphere()) != ContainmentType.Disjoint)
                return true;

            if (sphere.Contains(new BoundingSphere(endLocation, 25)) != ContainmentType.Disjoint)
                return true;

            foreach (BoundingBox b in wallsBoundingBoxes)
            {
                if (b.Contains(sphere) != ContainmentType.Disjoint)
                {
                    return true;
                }
            }
            return false;
        }
    }
}
